/*
 * In this algorithm, each process finds a word and mount his own tree
 * after finishing his step, it sends to root each word in his tree
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <mpi.h>
#include <omp.h>
#include "avl.h"

#define MAX 1000
#define ROOT 0

void send_words(node *t);
int palindrome(char*);
int removeSpace(char*);
void spaceToken(char*, char*, int, int);
void palindromeInWord(char*);

int
main(int argc, char *argv[])
{
    if(argc == 2)
        palindromeInWord(argv[1]);

    FILE *ifp; /* Input file pointer*/
    FILE *ofp; /* Output file pointer*/
    char *buf; /* buffer where the file will be loaded */
    char *buf2; /* buffer where the file will be loaded */
    char word[MAX]; /* string that represents each word */
    char aux[10001]; /* string aux*/
    int i; /* count variable */
    int j; /* aux variable */
    int k; /* aux variable */
    int res; /* aux variable */
    int sum; /* sum of each character */
    int low_value; /* low_value of each process */
    int high_value; /* high_value of each process */
    int size; /* size of each buffer */
    int id; /* id of process */
    int p; /* number of process */
    int tag = 0; /* tag for MPI_Recv e MPI_Send */
    int source; /* id source of the word */
    long f_size; /* size of the file */
    MPI_Status status; /* status for MPI_Recv*/
    MPI_Request req;
    /* Initialization of the MPI functions */
    MPI_Init(&argc, &argv);
    /* Important info for the program */
    MPI_Comm_rank(MPI_COMM_WORLD, &id);
    MPI_Comm_size(MPI_COMM_WORLD, &p);
    node *phrase = NULL; /* Initial node */

    /* Verifies if there is a file passed by argument */
    if(argc != 2)
    {
        printf("Usage: $big [file]");
        exit(1);
    }

    /* Tries to open the input file in read-only mode */
    puts("\n\nAbrindo o arquivo texto para procurar palavras....");
    ifp = fopen(argv[1], "r");

    if(ifp == NULL)
    {
        fprintf(stderr, "Could not open the file. Exiting.\n");
        exit(1);
    }

    if(id == ROOT)
    {
        /* Tries to open the output file in write mode */
        ofp = fopen("res_phrase_v3.txt", "w");

        if(ofp == NULL)
        {
            fprintf(stderr, "Could not create the file. Exiting.\n");
            exit(1);
        }

        /* Calculates the size of the file */
        fseek(ifp, 0, SEEK_END);
        f_size = ftell(ifp);
        rewind(ifp);

        /* Sends to every process the size of the file */
        for(i = 1; i < p; i++)
            MPI_Send(&f_size, 1, MPI_INT, i, tag, MPI_COMM_WORLD);
    }

    else
        MPI_Recv(&f_size, 1, MPI_INT, ROOT, tag, MPI_COMM_WORLD, &status);

    puts("Alocando memoria para o buffer principal....");
    buf = (char *) malloc(f_size * sizeof(char));

    if(buf == NULL)
    {
        fprintf(stderr, "Could not allocate memory. Exiting.\n");
        exit(1);
    }

    puts("Alocando memoria para o buffer auxiliar....");
    buf2 = (char *) malloc(f_size * sizeof(char));

    if(buf2 == NULL)
    {
        fprintf(stderr, "Could not allocate memory. Exiting.\n");
        exit(1);
    }

    puts("Copiando arquivo para o buffer principal....");
    res = fread(buf, 1, f_size, ifp);

    if(res != f_size)
    {
        fprintf(stderr, "Could not load the file. Exiting.\n");
        exit(1);
    }

    puts("Separando as iterações para cada processo....");

    if(id != ROOT)
    {
        int b,counter=0,len;

        //Verify if is puntuation to put spaces the text in phrases
        for(i = 0; i < f_size; i++)
        {
            if(ispunct(buf[i]) || isspace(buf[i]))
                buf[i] = ' ';
        }

        b = removeSpace(buf);
        printf("Letras no texto %d\n", b);
        /* Calculates the lower, the higher value and the size of the block */
        low_value = ((id - 1) * b / (p - 1));
        high_value = 1 + ((id) * b / (p - 1));
        size = high_value - low_value;
        int first, end;
        printf("Numero total de iteracoes %d no processo %d\n \
              iteracao menor %d iteracao maxima do processo %d\n",size,id,low_value,high_value);

        if(low_value <= 2)
            low_value = 2;

        for(j = low_value+1; j < high_value; j++)
        {
            first = 0;
            end = j;

            for(first = 0; end < b; first++)
            {
                spaceToken(buf, buf2, first, end);
                end++;

                if(find(buf2, phrase) == NULL && palindrome(buf2))
                {
                    char  word1[MAX];
                    len = strlen(buf2);
                    strcpy(word1,buf2);
                    phrase = insert(word1, phrase);
                    counter++;
                    MPI_Send(&len, 1, MPI_INT, ROOT, tag, MPI_COMM_WORLD);
                    MPI_Send(&word1, len, MPI_CHAR, ROOT, tag, MPI_COMM_WORLD);
                }
            }
        }

        printf("Palindromos encontrados %d no Processo %d\n",counter,id);
        /* if a process has terminated, sends -1 to root */
        i = -1;
        MPI_Send(&i, 1, MPI_INT, ROOT, tag, MPI_COMM_WORLD);
    }

    else
    {
        k = p - 1;

        /* while each process has not terminated keeps receiving words */
        while(k > 0)
        {
            /* receives from any source a word length */
            MPI_Recv(&i, 1, MPI_INT, MPI_ANY_SOURCE, tag,
                     MPI_COMM_WORLD, &status);
            source = status.MPI_SOURCE;

            /* if this length is not terminated flag */
            if(i > 0)
            {
                /* receives from the source a whole word */
                MPI_Recv(&word, i, MPI_CHAR, source, tag,
                         MPI_COMM_WORLD, &status);
                word[i] = '\0';

                /* search it in the avl tree */
                if(find(word, phrase) == NULL)
                {
                    fprintf(ofp, "%s\n",
                            word);
                    phrase = insert(word, phrase);
                }
            }

            /* if the termination flag has sent */
            else k--;
        }
    }

    /* frees the buffers and file pointers */
    free(buf);
    fclose(ifp);
    clear(phrase);

    if(id == ROOT)
        fclose(ofp);

    MPI_Finalize();
    puts("Limpando a memoria...,\n palindromos encontrados estao no arquivo res_phrase_v3.txt....");
    return 0;
}


int palindrome(char *word)
{
    int i;
    int res = 0;
    int len = strlen(word);

    for(i = 0; i < len; i++)
        if(word[i] == word[len - i - 1])
            res++;

    if(res == len)
        return 1;

    else
        return 0;
}

void
send_words(node *t)
{
    char word[MAX];
    int len;
    int tag = 0;

    if(t == NULL)
        return;

    if(t->left != NULL)
        send_words(t->left);

    if(t->right != NULL)
        send_words(t->right);

    len = strlen(t->word);
    strncpy(word, t->word, len);
    MPI_Send(&len, 1, MPI_INT, ROOT, tag, MPI_COMM_WORLD);
    MPI_Send(&word, len, MPI_CHAR, ROOT, tag, MPI_COMM_WORLD);
}

int removeSpace(char* buffer)
{
    int len, j = 0, i;
    len = strlen(buffer);

    for(i = 0; i < len; i++)
        if(buffer[i] != ' ')
        {
            buffer[j] = tolower(buffer[i]);
            j++;
        }

    buffer[j] = '\0';
    return j;
}

void spaceToken(char* buffer, char* buffer2, int first, int end)
{
    int len, i, j = 0;
    len = end - first;

    for(i = 0; i < len; i++)
        buffer2[i] = buffer[first + i];

    buffer2[len] = '\0';
    return;
}

void palindromeInWord(char* text)
{
    FILE * pFile;
    long lSize;
    char * buffer;
    size_t result;
    int i;
    char *pch;
    puts("Abrindo o arquivo texto para procurar palavras....");
    pFile = fopen(text , "rb");

    if(pFile==NULL)
    {
        fputs("File error",stderr);
        exit(1);
    }

    /* obtain file size: */
    fseek(pFile , 0 , SEEK_END);
    lSize = ftell(pFile);
    rewind(pFile);
    /* allocate memory to contain the whole file: */
    puts("Alocando memoria para o buffer principal....");
    buffer = (char*) malloc(sizeof(char)*lSize+1);

    if(buffer == NULL)
    {
        fputs("Memory error",stderr);
        exit(2);
    }

    /* copy the file into the buffer: */
    puts("Copiando arquivo para o buffer principal....");
    result = fread(buffer,1,lSize,pFile);

    if(result != lSize)
    {
        fputs("Reading error",stderr);
        exit(3);
    }

    /* the whole file is now loaded in the memory buffer. */
    /* terminate */
    fclose(pFile);
    /* Size of buffer */
    size_t b = lSize;
    /* To previne garbage */
    buffer[lSize] = '\0';
    puts("Separando o texto por palavras....");

    for(i=0; i < b ; i++)
    {
        buffer[i] = tolower(buffer[i]);

        if(ispunct(buffer[i]) || isspace(buffer[i]))
            buffer[i]='\n';
    }

    puts("Verificando os palindromos....");
    node *words = NULL;
    pFile = fopen("res_word_v2.txt" , "w");
    pch = strtok(buffer,"\n");

    while(pch != NULL)
    {
        if(find(pch,words)== NULL && palindrome(pch))
        {
            words = insert(pch,words);
            fprintf(pFile,"%s\n",pch);
        }

        pch = strtok(NULL,"\n");
    }

    puts("Limpando a memoria...,\n palindromos encontrados estao no arquivo res_word_v2.txt....");
    free(buffer);
    fclose(pFile);
    return;
}


